import { TSelectRanage, TMergeCell } from '../types/common.interface';
import { IStatus } from '../types/common.interface';
import { Injectable } from '../decorators/inject';
import { BaseCommand } from '../types/common.interface';
type TAfterSelectHook = (range: TSelectRanage) => void;
type TBeforeSelectHook = () => void|null;
type TPureFunc = () => void;

type TStatusChange = (status: Partial<IStatus>) => [Partial<IStatus>];
type TMergeCellHook = (range: TMergeCell) => [TMergeCell];
type TCellRender = (cellInfo: CellPosition, position: IPositionDetailInfo) => void;
class HookStore<T, S = any, ExcuteResult = any> {
    protected store: Array<T> = [];
    protected timmer: any = null;
    add(hook: T) {
        this.store.push(hook);
    }
    remove(hook: T) {
        for(let i = 0; i < this.store.length; i++) {
            if(this.store[i] == hook) {
                this.store.splice(i, 1);
                return true;
            }
        }
        return false;
    }
    excute(...args: any): ExcuteResult[] {
        let res = args;
        for(let hook of this.store) {
            let temp = (<any>hook)(...res);
            if(temp === null) {
                break;
            }
            if(temp !== undefined) {
                res = temp;
            }
        }
        return res;
    }
}
@Injectable('Hook')
export class Hook {
    public readonly afterSelect: HookStore<TAfterSelectHook> = new HookStore();
    public readonly beforeSelect: HookStore<TBeforeSelectHook> = new HookStore();
    public readonly afterTableInit: HookStore<TPureFunc> = new HookStore();
    public readonly statusChange: HookStore<TStatusChange> = new HookStore();
    public readonly beforeMergeCell: HookStore<TMergeCellHook> = new HookStore();
    public readonly afterMergeCell: HookStore<TMergeCellHook> = new HookStore();
    public readonly beforeUnMergeCell: HookStore<TMergeCellHook> = new HookStore();
    public readonly afterUnMergeCell: HookStore<TMergeCellHook> = new HookStore();
    public readonly afterBgRender: HookStore<any> = new HookStore();
    public readonly beforeCommandExcute: HookStore<{command: BaseCommand}, any, {command: BaseCommand}> = new HookStore();
    public readonly afterCommandExcute: HookStore<() => void> = new HookStore();
    public readonly beforeCommandRedoExcute: HookStore<{command: BaseCommand}> = new HookStore();
    public readonly afterCommandRedoExcute: HookStore<void> = new HookStore();
    public readonly beforeCellRender: HookStore<TCellRender> = new HookStore();
    public readonly afterCellRender: HookStore<TCellRender> = new HookStore();
    protected readonly customHook: Map<string, HookStore<void>> = new Map();
    public readonly beforeDataRender: HookStore<TCellRender> = new HookStore();
    public readonly afterDataRender: HookStore<TCellRender> = new HookStore();
    public readonly beforeRender: HookStore<(startTime: number) => void> = new HookStore();
    public readonly afterRender: HookStore<(endTime: number) => void> = new HookStore();
    public readonly beforeRenderColHeader: HookStore<() => void> = new HookStore();
    public readonly afterRenderColHeader: HookStore<() => void> = new HookStore();
    public readonly beforeRenderRowHeader: HookStore<() => void> = new HookStore();
    public readonly afterRenderRowHeader: HookStore<() => void> = new HookStore();
    public readonly beforeChangeFreezeRow: HookStore<(row: number) => void> = new HookStore();
    public readonly afterChangeFreezeRow: HookStore<(row: number) => void> = new HookStore();
    public readonly beforeChangeFreezeCol: HookStore<(row: number) => void> = new HookStore();
    public readonly afterChangeFreezeCol: HookStore<(row: number) => void> = new HookStore();
    public readonly beforeDeleteRow: HookStore<(row: number, rowInstance: any) => void> = new HookStore();
    public readonly afterDeleteRow: HookStore<(row: number, rowInstance: any) => void> = new HookStore();
    public readonly beforeInsertRow: HookStore<(row: number, rowObj: any) => void> = new HookStore();
    public readonly afterInsertRow: HookStore<(row: number, rowObj: any) => void> = new HookStore();
    public readonly beforeDeleteCol: HookStore<(col: number, cellInstance: any) => void> = new HookStore();
    public readonly afterDeleteCol: HookStore<(col: number, cellInstance: any) => void> = new HookStore();
    public readonly beforeInsertCol: HookStore<(col: number, colObj: any) => void> = new HookStore();
    public readonly afterInsertCol: HookStore<(col: number, colObj: any) => void> = new HookStore();
    public readonly pixelRatioChange: HookStore<(pixelRatio: number) => void> = new HookStore();
    public readonly beforeScaleChange: HookStore<(scale: number) => void> = new HookStore();
    public readonly afterScaleChange: HookStore<(scale: number) => void> = new HookStore();
    public readonly renderTime: HookStore<(scale: number) => void> = new HookStore();
    public readonly clickEvent: HookStore<(event: ExcelClickEvent) => void|null|[ExcelClickEvent]> = new HookStore();
    public readonly dbclickEvent: HookStore<(event: ExcelDbclickEvent) => void|null|[ExcelDbclickEvent]> = new HookStore();
    public readonly hoverEvent: HookStore<(event: ExcelHoverEvent) => void|null|[ExcelHoverEvent]> = new HookStore();
    public readonly mouseoverEvent: HookStore<(event: ExcelMouseoverEvent) => void|null|[ExcelMouseoverEvent]> = new HookStore();
    public readonly mouseoutEvent: HookStore<(event: ExcelMouseoutEvent) => void|null|[ExcelMouseoutEvent]> = new HookStore();
    public readonly mousedownEvent: HookStore<(event: ExcelMousedownEvent) => void|null|[ExcelMousedownEvent]> = new HookStore();
    public readonly mouseupEvent: HookStore<(event: ExcelMouseupEvent) => void|null|[ExcelMouseupEvent]> = new HookStore();
    public readonly tableDataChange: HookStore<() => void> = new HookStore();
    public readonly beforeColWidthChange: HookStore<(width: number) => void|[number]> = new HookStore();
    public readonly afterColWidthChange: HookStore<(width: number) => void|[number]> = new HookStore();
    public readonly beforeRowHeightChange: HookStore<(width: number) => void|[number]> = new HookStore();
    public readonly afterRowHeightChange: HookStore<(width: number) => void|[number]> = new HookStore();
    public createCustomHook(name: string): [null|string, HookStore<any>] {
        if(this.customHook.has(name)) {
            return ['hasHook', this.customHook.get(name)];
        }
        let _hook = new HookStore<any>();
        this.customHook.set(name, _hook);
        return [null, _hook];
    }
    public removeCustomHook(name: string) {
        this.customHook.delete(name);
    }
}